﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.BusinessData.Infrastructure;
using Microsoft.BusinessData.Runtime;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.MetadataModel.Collections;
using Microsoft.SharePoint.BusinessData.Administration;
using Microsoft.SharePoint.BusinessData.SharedService;

using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using MsExchangeBcsConnector.ExchangeBdcModel.Helpers;
using MsExchangeBcsConnector.ExchangeBdcModel;
using Microsoft.SharePoint.Administration;


namespace MsExchangeBcsConnector
{

    /// <summary>
    /// Custom BCS Connector for MS Exchange Server
    /// </summary>
    public class MsExchangeBcsConnector : IAdministrableSystem, ISystemUtility, IConnectionManager, IDisposable
    {

        #region IAdministrableSystem implementation

        public IList<AdministrableProperty> AdministrableLobSystemInstanceProperties
        {
            get {
                return new List<AdministrableProperty>()
                {
                    new AdministrableProperty(Constants.ProfileSiteUrlPropertyName, "URL of sharepoint web application with associated User Profile Service Application",
                        typeof(string),Constants.ProfileSiteUrlPropertyName,typeof(string), true),
                    new AdministrableProperty(Constants.EmailDomainForCrawlingPropertyName, "Exchange web service login domain name connect to.",
                        typeof(string),Constants.EmailDomainForCrawlingPropertyName,typeof(string), true),
                    new AdministrableProperty(Constants.ExchangeServiceUrlPropertyName, @"Exchange web service URL. For example: https://mail.contoso.com/EWS/Exchange.asmx",
                        typeof(string),Constants.ExchangeServiceUrlPropertyName,typeof(string), true),
                    new AdministrableProperty(Constants.ExchangeLoginPropertyName, "Exchange web service login. This account should have access to all mailboxes.",
                        typeof(string),Constants.ExchangeLoginPropertyName,typeof(string), true),
                    new AdministrableProperty(Constants.ExchangePasswordPropertyName, "Exchange web service password.",
                        typeof(string),Constants.ExchangePasswordPropertyName,typeof(string), true),
                    new AdministrableProperty(Constants.AdministratorSecurityGroupPropertyName,"Ad Security group for read all mailboxes access",typeof(string),
                        Constants.AdministratorSecurityGroupPropertyName, typeof(string), true)
                     /*   ,
                    new AdministrableProperty(Constants.MailboxDisplayUrlPattern,
                            @"Mailbox display URL pattern in format: http://hostname/path/MailBox.aspx?EMail={0} ",typeof(string),
                        Constants.MailboxDisplayUrlPattern, typeof(string), true),
                    new AdministrableProperty(Constants.MessageDisplayUrlPattern,
                            @"Mailbox display URL pattern in format: http://hostname/path/Message.aspx?MessageId={0}&EMail={1} ",typeof(string),
                        Constants.MessageDisplayUrlPattern, typeof(string), true)*/
                };

                }                        
        }

        public IList<AdministrableProperty> AdministrableLobSystemProperties
        {
            get { 
                   return new List<AdministrableProperty>() { 
                   new AdministrableProperty("TestProperty", "URL of sharepoint web application with associated User Profile Service Application",
                        typeof(string),"TestProperty",typeof(string), true)                   
                   };
                }
        }


        #endregion


        #region ISystemUtility implementation

        public IEnumerator CreateEntityInstanceDataEnumerator(object rawStream, ISharedEntityState sharedEntityState)
        {
            IEnumerable enumerableStream = rawStream as IEnumerable; 
            if (enumerableStream != null) { 
                return enumerableStream.GetEnumerator(); 
            } 
            throw new InvalidOperationException("not valid stream returned");

        }

        public IConnectionManager DefaultConnectionManager
        {
            get {
                return null; //Let's SharePoint do it!
                }
        }

        public ITypeReflector DefaultTypeReflector
        {
            get { 
                    return null; //Use default
                }
        }

        public void ExecuteStatic(IMethodInstance methodInstance, ILobSystemInstance si, object[] methodSignatureArgs, IExecutionContext context)
        {

            if (methodInstance == null)
            {
                LoggingService.LogCritical("Required parameter missing: \"{0}\"", "methodInstance");
                throw (new ArgumentNullException("methodInstance"));
            }
            if (si == null)
            {
                LoggingService.LogCritical("Required parameter missing: \"{0}\"", "lobSystemInstance");
                throw (new ArgumentNullException("lobSystemInstance"));
            }
            if (methodSignatureArgs == null)
            {
                LoggingService.LogCritical("Required parameter missing: \"{0}\"", "args");
                throw (new ArgumentNullException("args"));
            }

            if (this.profileSite == null)
            {
                this.Initialize(si);
            }

            //Implement Methods
            switch (methodInstance.MethodInstanceType)
            {
                case MethodInstanceType.IdEnumerator:         
                case MethodInstanceType.Finder:
                    ExecuteIdEnumerator(si, methodInstance, methodSignatureArgs);
                    break;
                case MethodInstanceType.SpecificFinder:
                    ExecuteSpecificFinder(si, methodInstance, methodSignatureArgs);
                    break;
                case MethodInstanceType.AssociationNavigator:
                    ExecuteAssociationNavigator(si, methodInstance, methodSignatureArgs);
                    break;
                case MethodInstanceType.BinarySecurityDescriptorAccessor:
                    ExecuteBinarySecurityDescriptorAccessor(si, methodInstance, methodSignatureArgs);
                    break;
                case MethodInstanceType.StreamAccessor:
                    ExecuteStreamAccessor(si, methodInstance, methodSignatureArgs);
                    break;              
                default:
                    {
                        LoggingService.LogCritical("Method Instance Type \"{0}\" not implemented.", methodInstance.MethodInstanceType);
                        throw new Exception(String.Format("Method Instance Type \"{0}\" not implemented.", methodInstance.MethodInstanceType));
                    }
            }


        }

        #endregion


        #region Private methods

        private void ExecuteIdEnumerator(ILobSystemInstance si, IMethodInstance methodInstance, object[] args)
        {

            String methodName = methodInstance.GetMethod().Name;

            switch(methodName){

                case  Constants.ReadMailBoxList_MethodName:
                        UserProfileManager UPService = (UserProfileManager)this.GetConnection();
                        List<MailBox> mailboxes = UserProfileHelper.LoadMailBoxList(UPService);
                        EnshureAccessCached(mailboxes, si);
                        args[0] = mailboxes;                        
                        UPService = null;
                        break;
                case Constants.ReadMailboxFoldersList_MethodName:
                        string EMailAddress = args[0] as string;
                        //EnshureAccessCached(EMailAddress, si);
                        args[1] = ExchangeHelper.ReadMailboxFoldersList(si, EMailAddress);                    
                        break;
                default:
                        {
                            LoggingService.LogCritical("ExecuteIdEnumerator Method name \"{0}\" not implemented.", methodName);
                            throw new Exception(String.Format("ExecuteIdEnumerator Method name \"{0}\" not implemented.", methodName));
                        }
            }
           
           
        }

        private void ExecuteStreamAccessor(ILobSystemInstance si, IMethodInstance methodInstance, object[] args)
        {
            IMethod  method = methodInstance.GetMethod();
            string methodName = method.Name;
            // Every specific finder should have this parameter
            switch (methodName)
            {
                case Constants.AttachmentStream_MethodName:
                    string EMailAddress = args[0] as string;
                    string MessageId = args[1] as string;
                    string AttachmentID = args[2] as string;
                    //EnshureAccessCached(EMailAddress, si);
                    Attachment attachFile = ExchangeHelper.LoadAttachement(si, AttachmentID, MessageId, EMailAddress, true, this.BcsProfile_HostURL);
                    args[3] = attachFile == null ? null : attachFile.AttachmentStream;
                    break;
                default:
                    {
                        LoggingService.LogCritical("StreamAccessor Method name \"{0}\" not implemented.", methodName);
                        throw new Exception(String.Format("StreamAccessor Method name \"{0}\" not implemented.", methodName));
                    }
            }
        }

        private void ExecuteBinarySecurityDescriptorAccessor(ILobSystemInstance si, IMethodInstance methodInstance, object[] args){
            IMethod method = methodInstance.GetMethod();
            string methodName = method.Name;

              byte [] sd = MailBoxSecurityCache.GetMailboxSecurity(args[0] as string);
            if (sd == null){
                string AdministratorSecurityGroup = LobSystemInstanceHelper.AdministratorSecurityGroup(si);
                UserProfileManager UPService = (UserProfileManager)this.GetConnection();
                MailBox SecureTemplate = UserProfileHelper.LoadMailBox(UPService, args[0] as string);
                SecureTemplate.AddLogin(AdministratorSecurityGroup);

                sd = MailBoxSecurityCache.CacheMailboxSecurity(SecureTemplate);
            }

            switch (methodName)
            {
                case Constants.ReadMailBoxSecurityDescriptor_MethodName:
                        args[2] = sd;// MailBoxSecurityCache.GetMailboxSecurity(args[0] as string);
                        break;
                case Constants.ReadMailFolderSecurityDescriptor_MethodName: 
                case Constants.ReadMessageSecurityDescriptor_MethodName:
                       args[3] = sd;//MailBoxSecurityCache.GetMailboxSecurity(args[0] as string);
                       break;
                case Constants.ReadAttachmentSecurityDescriptor_MethodName:
                       args[4] = sd;//MailBoxSecurityCache.GetMailboxSecurity(args[0] as string);
                       break;
                default:
                       {
                           LoggingService.LogCritical("BinarySecurityDescriptorAccessor Method name \"{0}\" not implemented.", methodName);
                           throw new Exception(String.Format("BinarySecurityDescriptorAccessor Method name \"{0}\" not implemented.", methodName));
                       }
            }
            
        }


      private void EnshureAccessCached(List<MailBox> SecureTemplates, ILobSystemInstance si)
        {
            string AdministratorSecurityGroup = LobSystemInstanceHelper.AdministratorSecurityGroup(si);            
            foreach(MailBox SecureTemplate in SecureTemplates){
            if (MailBoxSecurityCache.GetMailboxSecurity(SecureTemplate.EMail) != null)
                return; // got it            
            SecureTemplate.AddLogin(AdministratorSecurityGroup);

            MailBoxSecurityCache.CacheMailboxSecurity(SecureTemplate);
            }
        }

        private void ExecuteSpecificFinder(ILobSystemInstance si, IMethodInstance methodInstance, object[] args)
        {

            IMethod  method = methodInstance.GetMethod();
            string methodName = method.Name;
            // Every specific finder should have this parameter
            

            switch (methodName)
            {

                case Constants.ReadMailBox_MethodName:
                    string EMailAddress = args[0] as string;
                    UserProfileManager UPService = (UserProfileManager)this.GetConnection();
                    MailBox retVal = UserProfileHelper.LoadMailBox(UPService, EMailAddress);
                    //EnshureAccessCached(new List<MailBox> { retVal }, si);
                    args[1] = retVal;                    
                    UPService = null;
                    break;
                case Constants.ReadMailFolder_MethodName:
                    string MailFolderId = args[0] as string;
                     EMailAddress = args[1] as string;
                     //EnshureAccessCached(EMailAddress, si);
                     args[2] = ExchangeHelper.ReadSpecificMailFolder(si, EMailAddress, MailFolderId);
                     break;
                case Constants.ReadMailboxMessage_MethodName:
                     string MessageId = args[0] as string;
                     EMailAddress = args[1] as string;
                    // EnshureAccessCached(EMailAddress, si);
                     args[2] = ExchangeHelper.LoadMessage(si, MessageId, EMailAddress, this.BcsProfile_HostURL);
                     break;
                case Constants.GetSpecifiedAttachment_MethodName:                
                        EMailAddress = args[0] as string;
                        MessageId = args[1] as string;
                        string AttachmentID = args[2] as string;
                     //   EnshureAccessCached(EMailAddress, si);
                        Attachment attachFile = ExchangeHelper.LoadAttachement(si, AttachmentID, MessageId, EMailAddress, false, this.BcsProfile_HostURL);
                        args[3] = attachFile;
                       break;
                default:
                       {
                           LoggingService.LogCritical("Specific Finder Method name \"{0}\" not implemented.", methodName);
                           throw new Exception(String.Format("Specific Finder Method name \"{0}\" not implemented.", methodName));
                       }
            }
        }


        private void ExecuteAssociationNavigator(ILobSystemInstance si, IMethodInstance methodInstance, object[] args)
        {
            IMethod method = methodInstance.GetMethod();
            string methodName = method.Name;
            // Every association should have this parameter
            string EMailAddress;

            switch (methodName)
            {

                case Constants.MailBoxToMailFolder_MethodName:
                    EMailAddress = args[0] as string;
                    //EnshureAccessCached(EMailAddress, si);
                    args[1] = ExchangeHelper.ReadMailboxFoldersList(si, EMailAddress);                    
                    break;
                case Constants.MailFolderToMessage_MethodName:
                    int? offset = args[0] as int?;
                    DateTime? modifiedSince = args[1] as DateTime?;
                    EMailAddress = args[2] as string;
                    string MailFolderID = args[3] as string;
                    //EnshureAccessCached(EMailAddress, si);
                    MailBatch batch = new MailBatch(MailFolderID, EMailAddress, offset);
                    ExchangeHelper.ReadFolderMessagesList(si, this.BcsProfile_HostURL, ref batch);
                    args[4] = batch;
                   // args[2] = DateTime.Now;
                    break;
                case Constants.GetAllAttachments_MethodName:
                    EMailAddress = args[0] as string;
                    string MessageId = args[1] as string;
                    //EnshureAccessCached(EMailAddress, si);
                    //bool HasAttachments = args[2] as boole;
                    args[2] = ExchangeHelper.ReadMessageAttachmentsList(si, this.BcsProfile_HostURL, MessageId, EMailAddress);
                    break;
                default:
                    {
                        LoggingService.LogCritical("AssociationNavigator method name \"{0}\" not implemented.", methodName);
                        throw new Exception(String.Format("AssociationNavigator method name \"{0}\" not implemented.", methodName));
                    }
            }
        }


 

        #endregion


        #region IConnectionManager Members

            SPSite profileSite = null;
            string BcsProfile_HostURL = null;

            public void CloseConnection(object connection)
            {
                if (connection != null)
                        connection = null;
            }

            public void FlushConnections()
            {
                // do nothing
            }

            public object GetConnection()
            {
                
                SPServiceContext serviceContext = Microsoft.SharePoint.SPServiceContext.GetContext(profileSite);                
                return new UserProfileManager(serviceContext);
            }

            /// <summary>
            /// Initialize new SPSite to get UserProfileManager from it
            /// </summary>
            /// <param name="lobSystemInstance"></param>
            public void Initialize(ILobSystemInstance lobSystemInstance)
            {
                // Get URL of web application with USer Profile Service application                
                this.profileSite = new SPSite(LobSystemInstanceHelper.ProfileSiteUrl(lobSystemInstance));

                // Initialize Profile_HostURL property
               
                SPServiceContext serviceContext = Microsoft.SharePoint.SPServiceContext.GetContext(profileSite);
                BdcServiceApplicationProxy proxy = (BdcServiceApplicationProxy)serviceContext.GetDefaultProxy(typeof(BdcServiceApplicationProxy));
                AdministrationMetadataCatalog AdminCatalog = proxy.GetAdministrationMetadataCatalog();

                Microsoft.SharePoint.BusinessData.Administration.Property property = AdminCatalog.Properties.FirstOrDefault(p => p.Name.Equals(Constants.Profile_HostURL_Property));
                if (property != null && !string.IsNullOrEmpty(property.Value as string))
                {
                    this.BcsProfile_HostURL = property.Value.ToString();
                }
                else
                {
                    this.BcsProfile_HostURL = this.profileSite.Url;
                }


            }

        #endregion

            public void Dispose()
            {
                if (profileSite != null)
                {
                    profileSite.Close();
                    profileSite = null;
                }
            }
    }
}
